package org.wheres2;

import org.wheres2.R;
import org.wheres2.core.ServiceClient;
import org.wheres2.eventmanager.ConnectionErrorManager;
import org.wheres2.eventmanager.PositionManager;
import org.wheres2.location.ServiceLocation;
import org.wheres2.location.customview.AnimView;
import org.wheres2.location.customview.CompassView;
import org.wheres2.location.customview.PositionView;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 
 * La classe implementa l'activity Radar
 * 
 * @author 
 *
 */
public class Radar extends Activity implements PositionManager, SensorEventListener,
												ConnectionErrorManager{

	
	public static final String LOGIN_THROWER = "org.wheres2.Radar.Login";
	public static final String CONTACT_THROWER = "org.wheres2.Radar.ContactIntent";
	public static final String CONTACT_LIST_THROWER = "org.wheres2.Radar.ContactList";
	public static final String THROWER = "org.wheres2.Radar.Thrower";
	public static final String CONTACT = "org.wheres2.Radar.Contact";
	public static final String CONTACT_LATITUDE = "org.wheres2.Radar.ContactLatitude";
	public static final String CONTACT_LONGITUDE = "org.wheres2.Radar.ContactLongitude";
	public static final String CONTACT_ACCURACY = "org.wheres2.Radar.ContactAccuracy";
	
	private PowerManager.WakeLock wakeLock;
	
	
	private TextView destLatitudeText;
	private TextView destLongitudeText;
	private TextView destAccuracyText;
	private TextView myLatitudeText;
	private TextView myLongitudeText;
	private TextView myAccuracyText;
	
	private TextView advisor;
	private TextView dist;
	private AnimView animView;
	private PositionView pView;
	private CompassView cView;
	private Animation scale;
	private Button findButton;
	private Button saveButton;
	
	private SharedPreferences shared;
	private SharedPreferences.Editor editor;
	
	private Location location;
	
	private double myLatitude=0;
	private double myLongitude=0;
	private int azimuth; //rotation around the Z axis   View http://developer.android.com/reference/android/hardware/SensorManager.html#getOrientation(float[], float[])
	private int alfa; // angolo rispetto all'asse nord
	private Location destLocation;
	private double destLatitude=0;
	private double destLongitude=0;
	private float destAccuracy=0;
	private boolean errorConnection = false;
	
	private String distance;

	private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private Sensor magnetic;
    private float[] mGData = new float[3];
    private float[] mIData = new float[3];
    private float[] mR = new float[16];
    private float[] mOrientation = new float[3];
    
    private String thrower;
    
    private Intent locationServiceIntent;
	private ServiceLocation serviceLocation;
	private boolean boundToLocationService;
	
    

	private ServiceConnection locatorConnection = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			serviceLocation = ((ServiceLocation.ServiceLocationBinder)service).getService();
			if(serviceLocation.isGPSProviderEnabled() || serviceLocation.isNetProviderEnabled()){
				serviceLocation.startLocation();
				serviceLocation.addPositionListener(Radar.this);
			}else{
				createNoLocationDialog();
			}
			location = serviceLocation.getLocation();
			boundToLocationService = true;
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			serviceLocation = null;
			boundToLocationService = false;
		}	
		
	};
	
		
	private Intent clientServiceIntent;
	private ServiceClient client;
	private boolean boundToClientService;
	
	
	private ServiceConnection clientConnection = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			client = ((ServiceClient.ServiceClientBinder)service).getService();
			if(client.isGPSProviderEnabled() || client.isNetProviderEnabled()){
				client.addConnectionErrorListener(Radar.this);
				client.startLocation();
				client.addPositionListener(Radar.this);
			}else{
				createNoLocationDialog();
			}
			location = client.getLocation();
			boundToClientService = true;
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			client = null;
			boundToClientService = false;
		}	
		
	};
	
	/**
	 * 
	 * @see android.hardware.SensorEventListener#onAccuracyChanged(Sensor, int)
	 * 
	 */
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		
	}
	
	/**
	 * 
	 * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
	 * 
	 */
	@Override
	public void onSensorChanged(SensorEvent event) {
		
		int type = event.sensor.getType();
		int pitch = 0;
		if (type == Sensor.TYPE_ACCELEROMETER) {	
			for (int i=0 ; i<3 ; i++)
				mGData[i] = event.values[i];
		}else if(type == Sensor.TYPE_MAGNETIC_FIELD){
			for (int i=0 ; i<3 ; i++)
				mIData[i] = event.values[i];

		}else{
			return;
		}

		if(mGData != null && mIData != null){
			SensorManager.getRotationMatrix(mR, null,mGData , mIData);
			SensorManager.getOrientation(mR, mOrientation);
			azimuth = (int) Math.toDegrees(mOrientation[0]);
			pitch = (int) Math.toDegrees(mOrientation[1]);
		}
		if(pitch > 45 || pitch < -45){
			if(advisor.getVisibility()==View.INVISIBLE)
				advisor.setVisibility(View.VISIBLE);	
		}else{
			if(advisor.getVisibility() == View.VISIBLE)
				advisor.setVisibility(View.INVISIBLE);
			
		}
		cView.setAzimuth(azimuth);
		cView.invalidate();
		pView.invalidate();
	}

	@SuppressWarnings("deprecation")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_radar);
		this.destLocation = new Location(LocationManager.GPS_PROVIDER);
		PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
		this.wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "myTag");
		this.wakeLock.acquire();
		
		mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
		mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		magnetic= mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
		
		Intent throwerIntent = getIntent();		

		initViews();
		
		scale = AnimationUtils.loadAnimation(this, R.anim.anim);
		thrower = throwerIntent.getStringExtra(Radar.THROWER);
		if(thrower.equals(Radar.LOGIN_THROWER) || thrower.equals(Radar.CONTACT_LIST_THROWER)){
			
			pView.setVisibility(View.INVISIBLE);
			shared = this.getPreferences(Context.MODE_PRIVATE);
			editor = shared.edit();
			distance = "0";
			
			destLatitudeText.setText(shared.getString("lat", "0.0"));
			destLongitudeText.setText(shared.getString("long", "0.0"));
			destAccuracyText.setText(shared.getString("acc", "0.0"));
			
			locationServiceIntent = new Intent(Radar.this, ServiceLocation.class);
			startService(locationServiceIntent);
			bindService(locationServiceIntent, locatorConnection, Context.BIND_AUTO_CREATE);
			
		}else if(thrower.equals(Radar.CONTACT_THROWER)){
			
			destLatitude = throwerIntent.getDoubleExtra(Radar.CONTACT_LATITUDE, 0.0);
			destLongitude = throwerIntent.getDoubleExtra(Radar.CONTACT_LONGITUDE, 0.0);
			destAccuracy = throwerIntent.getFloatExtra(Radar.CONTACT_ACCURACY, 0);
			
			this.destLocation.setLatitude(destLatitude);
			this.destLocation.setLongitude(destLongitude);
			this.destLocation.setAccuracy(destAccuracy);
			
			destLatitudeText.setText("" + destLatitude);
			destLongitudeText.setText("" + destLongitude);
			destAccuracyText.setText("" + destAccuracy);
			
			saveButton.setVisibility(View.INVISIBLE);
			findButton.setVisibility(View.INVISIBLE);
			clientServiceIntent = new Intent(Radar.this, ServiceClient.class);
			bindService(clientServiceIntent, clientConnection, Context.BIND_AUTO_CREATE);
			
			animView.startAnimation(scale);
			
			
		}
	
	}
	
	
	@Override
	protected void onResume() {
		super.onResume();
		//registro il listener del sensore
		mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
		mSensorManager.registerListener(this, magnetic, SensorManager.SENSOR_DELAY_NORMAL);
		
		
		if(!this.boundToLocationService && thrower.equals(Radar.LOGIN_THROWER)){
			bindService(locationServiceIntent, locatorConnection, Context.BIND_AUTO_CREATE);
		}else if(!this.boundToClientService && thrower.equals(Radar.CONTACT_THROWER)){
			bindService(clientServiceIntent, clientConnection, Context.BIND_AUTO_CREATE);
		}
		
		if(location != null){
			updateLocationData(location);
			
		}
		
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		mSensorManager.unregisterListener(this);
		
		if(this.boundToLocationService){
			this.serviceLocation.stopLocation();
			unbindService(locatorConnection);
			this.boundToLocationService = false;
		}
		
		if(this.boundToClientService){
			this.client.removeConnectionErrorListener(this);
			this.client.stopLocation();
			unbindService(clientConnection);
			this.boundToClientService = false;
		}
		this.wakeLock.release();
	}
		
	/**
	 * 
	 * Operazioni associate al pulsante "Trova"
	 * @param view
	 */
	public void onClickFind(View view){
		
		if(location != null){
			animView.startAnimation(scale);	
			destLatitude = Double.parseDouble(shared.getString("lat", "0.0"));
			destLongitude = Double.parseDouble(shared.getString("long", "0.0"));
			destAccuracy = Float.parseFloat(shared.getString("acc", "0.0"));
			
			
			this.destLocation.setLatitude(destLatitude);
			this.destLocation.setLongitude(destLongitude);
			this.destLocation.setAccuracy(destAccuracy);
			
			Log.i("prova", destLatitude + " " + destLongitude + " " + destAccuracy);
			pView.setVisibility(View.VISIBLE);
			updateLocationData(location);
		}else
			Toast.makeText(this, R.string.LocationWaiting, Toast.LENGTH_SHORT).show();
		
	}
	
	/**
	 * Operazioni associate al pulsante "memorizza"
	 * 
	 * @param view
	 */
	public void onClickSave(View view){
		String latitude;
		String longitude;
		String accuracy;
		if(location != null){
			latitude = Double.toString(location.getLatitude());
			longitude = Double.toString(location.getLongitude());
			accuracy = Float.toString(location.getAccuracy());	
			editor.putString("lat", latitude);
			editor.putString("long", longitude);
			editor.putString("acc", accuracy);
			editor.commit();
			destLatitudeText.setText(latitude);
			destLongitudeText.setText(longitude);
			destAccuracyText.setText(accuracy);
			
		}
		else
			Toast.makeText(this, R.string.LocationWaiting, Toast.LENGTH_LONG).show();
	}
	
	/**
	 * 
	 * Aggiorna la posizione corrente e aggiorna la View
	 * 
	 * @param location La posizione GPS nel formato android.location.Location
	 */
	public void updateLocationData(Location location){
		
		this.location = location;
		float[] directDist = new float[3];		
		
		myLatitude = location.getLatitude();
		myLongitude = location.getLongitude();

		
		Location.distanceBetween(myLatitude, myLongitude, destLatitude, destLongitude, directDist);
		distance = String.valueOf(directDist[0]);
		dist.setText(distance+"mt");
		
		
		alfa= (int) location.bearingTo(destLocation);

		pView.coordinate(directDist[0]);
		pView.setGradi(alfa+azimuth);
		pView.invalidate(); 
		
	}
	
	/**
	 * 
	 * Visualizsa la dialog relativa all'assenza della localizzazione
	 * 
	 */
	private void createNoLocationDialog(){
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.NoPositionProviderDialogMessage)
				.setPositiveButton(R.string.OpenSettingsDialogButton, new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);						
			}
		})
		.setNegativeButton(R.string.ReloadDialogButton, new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				closeOnNoLocation();
				dialog.dismiss();
			
			}
		});
		AlertDialog noLocation = builder.create();
		noLocation.show();
	}
	
	/**
	 * 
	 * Chiude l'applicazione
	 * 
	 */
	private void closeOnNoLocation(){
		this.finish();
	}

	/**
	 * 
	 * Inizializza la View
	 */
	private void initViews(){
		
		destLatitudeText = (TextView) findViewById(R.id.FixedLatTextView);
		destLongitudeText = (TextView) findViewById(R.id.FixedLongTextView);
		destAccuracyText  = (TextView) findViewById(R.id.FixedAccTextView);
		myLatitudeText = (TextView) findViewById(R.id.CurrentLatTextView);
		myLongitudeText = (TextView) findViewById(R.id.CurrentLongTextView);
		myAccuracyText = (TextView) findViewById(R.id.CurrentAccTextView);
		advisor = (TextView) findViewById(R.id.advisorText);
		advisor.setVisibility(View.INVISIBLE);
		dist = (TextView) findViewById(R.id.DistanceMeter);
		animView = (AnimView) findViewById(R.id.AnimView);
		findButton = (Button) findViewById(R.id.find);
		saveButton = (Button) findViewById(R.id.savePos);
		pView = (PositionView) findViewById(R.id.PositionView);
		cView=(CompassView) findViewById(R.id.CompassView);
		
		
	}
	
	/**
	 * 
	 * @see org.wheres2.eventmanager.PositionManager#updateLocation(Location)
	 * 
	 */
	@Override
	public void updateLocation(Location location) {
		myLatitudeText.setText("" + location.getLatitude());
		myLongitudeText.setText("" + location.getLongitude());
		myAccuracyText.setText("" + location.getAccuracy());
		this.updateLocationData(location);
		
	}
	
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
	    if ((keyCode == KeyEvent.KEYCODE_BACK)) {
	    	if(this.boundToLocationService){
	    		if(this.serviceLocation.isLocationStarted())
	    			this.serviceLocation.stopLocation();
	    		unbindService(locatorConnection);
	    		this.boundToLocationService = false;
	    		this.serviceLocation.stopSelf();    		
	    		}
	    	if(this.boundToClientService){
	    		this.client.stopLocation();
	    		this.client.stopLocationService();
	    		unbindService(clientConnection);
	    		this.boundToClientService = false;
	    		this.client.stopSelf();
	    	}
	    	if(this.errorConnection){
		        Intent i = new Intent(Intent.ACTION_MAIN);
		        i.addCategory(Intent.CATEGORY_HOME);
		        startActivity(i);
		        return false;
	    	}
	        }
		return super.onKeyDown(keyCode, event);
	}

	/**
	 * 
	 * @see org.wheres2.eventmanager.ConnectionErrorManager#closeOnConnectionError()
	 * 
	 */
	@Override
	public void closeOnConnectionError() {
		this.errorConnection = true;
			runOnUiThread(new Runnable() {
				
				@Override
				public void run() {
					close();
					
				}
			});
			
	}
	
	/**
	 * 
	 * Operazioni eseguite a seguito di un errore nella connessione
	 * 
	 */
	private void close(){
		
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage(R.string.ConnectionErrorDialogText)
					.setTitle(R.string.ConnectionErrorDialogTitle)
					.setPositiveButton("OK", new DialogInterface.OnClickListener() {
				
						@Override
						public void onClick(DialogInterface dialog, int which) {
							dialog.dismiss();
				}
			});
			AlertDialog connectionError = builder.create();
			connectionError.show();	
			
	}
	
	
	
	


}
